/* Copyright 2001,2002,2003 NAH6 BV
 * All Rights Reserved
 *
 *  $Header: /var/lib/cvs/secphone/ui/rng/sources/esrcAudioInput.cpp,v 1.5 2003/11/21 16:12:58 itsme Exp $
 *
 *
 * this module implements entropy gathering from the microphone input
 *
 * it is turned on at application startup, if no entropy seedfile was found,
 * and during call setup.
 *
 */


#include "esrcAudioInput.h"

void DumpWH(WAVEHDR *pwh)
{
    debug("pwh=%08lx data=%08lx rec=%08lx len=%08lx flags=%x loops=%x usr=%x\n", 
        pwh, pwh->lpData, pwh->dwBytesRecorded, pwh->dwBufferLength, pwh->dwFlags, pwh->dwLoops, pwh->dwUser);
}

esrcAudioInput::esrcAudioInput()
    : m_queue(4)
{
    m_samplesperblock= 4410;     // 1/10 second per sampleblock
    m_samplerate= 44100;
    m_nBlocks= 4;

    m_nAudioBlocksProcessed= 0;
}

esrcAudioInput::~esrcAudioInput()
{
}

bool esrcAudioInput::CopyInputBuffer( WAVEHDR *pwh)
{
/*
    ByteVector lowordererbits;

    // todo: figure out if this is a sensible thing to do:
    //    - extract 2 low order bits from each sample, to
    //      reduce the amount of data to hash to 1/8.
    //    - or just hash it all.
    BYTE bits=0;
    for (size_t i=0 ; i<pwh->dwBytesRecorded/sizeof(sample_t) ; i++)
    {
        bits |= (pwh->lpData[i]&3) << (2*(i&3));

        if ((i&3)==3)
            lowordererbits.push_back(bits);
    }

    AddRandom(HashFunction::Calculate(lowordererbits));
*/
    AddRandom(HashFunction::Calculate(ByteVector((BYTE*)pwh->lpData, (BYTE*)pwh->lpData+pwh->dwBytesRecorded)));
    m_nAudioBlocksProcessed++;
    return true;
}
void esrcAudioInput::HandleSource()
{
    WAVEHDR *pwh;

    while (m_queue.GetNoWait(pwh))
    {           
        if (pwh==NULL)
            continue;

        CopyInputBuffer( pwh );

        //debug("before unprepare: "); DumpWH(pwh);

        if (m_bRunning)
        {
            UnPrepareInputBuffer( pwh );
            //debug("after unprepare: "); DumpWH(pwh);
        }
        else
            debug("not unpreparing\n");


        if (m_bRunning)
        {
            PrepareAndSubmitBuffer(pwh->dwUser);
            //debug("after prepare: "); DumpWH(pwh);
        }
        else
        {
            debug("esrcAudioInput::HandleSource  stopping, dropped buffer %d\n", pwh->dwUser);
        }
    }
}

bool esrcAudioInput::IsPolledSource() const
{
    return false;
}

void CALLBACK esrcAudioInput::waveInProc(
        HWAVEIN hwi, UINT uMsg, DWORD dwInstance, 
        DWORD dwParam1, DWORD dwParam2)
{
    esrcAudioInput* obj= (esrcAudioInput*)dwInstance;
    obj->HandleWaveCallback(uMsg, (WAVEHDR*)dwParam1);
}
void esrcAudioInput::HandleWaveCallback(UINT uMsg, WAVEHDR* pwh)
{
    switch(uMsg)
    {
    case WIM_OPEN:
        m_bRunning= true;
        SignalNewData();
        break;
    case WIM_DATA:
        m_queue.Add(pwh);
        SignalNewData();
        break;
    case WIM_CLOSE:
        m_bRunning= false;
        SignalNewData();
        break;
    }
}

bool esrcAudioInput::UnPrepareInputBuffer( WAVEHDR *pwh )
{
    MMRESULT res= waveInUnprepareHeader( m_hwi, pwh, sizeof( WAVEHDR ) );
    if( res!=MMSYSERR_NOERROR )
    {
        debug("ERROR: waveInUnprepareHeader: %08lx\n", res);
        return false;
    }
    return true;
}
bool esrcAudioInput::PrepareAndSubmitBuffer(int buffernr)
{
    m_bufferlist[buffernr].resize(m_samplesperblock);

    WAVEHDR *pwh= &m_whlist[buffernr];

    pwh->dwBytesRecorded= 0;
    pwh->dwFlags= 0;
    pwh->dwLoops= 0;
    pwh->dwUser= buffernr;
    pwh->dwBufferLength= m_samplesperblock*sizeof(sample_t);

    //debug("srcAudioInput::PrepareAndSubmitBuffer:  vptr=%08lx  data=%08lx\n", vectorptr(m_bufferlist[buffernr]), pwh->lpData);

    pwh->lpData= (char*)vectorptr(m_bufferlist[buffernr]);

    //debug("inside prepare: "); DumpWH(pwh);

    DWORD res= waveInPrepareHeader( m_hwi, pwh, sizeof( WAVEHDR ) );
    if( res!=MMSYSERR_NOERROR )
    {
        debug("ERROR waveInPrepareHeader: %08lx\n", res);
        return false;
    }
    //debug("inside prepare2: "); DumpWH(pwh);
    res= waveInAddBuffer(m_hwi, pwh, sizeof( WAVEHDR ) );
    if (res!=MMSYSERR_NOERROR )
    {
        debug("ERROR waveInAddBuffer: %08lx\n", res);
        return false;
    }
    //debug("exiting prepare: "); DumpWH(pwh);
    return true;
}

bool esrcAudioInput::StartAudioInput()
{
    WAVEFORMATEX wfe;

    wfe.nSamplesPerSec= m_samplerate;
    wfe.wFormatTag= WAVE_FORMAT_PCM;
    wfe.nChannels= 1;
    wfe.wBitsPerSample= 16;
    wfe.nBlockAlign= (wfe.nChannels*wfe.wBitsPerSample)/8;
    wfe.nAvgBytesPerSec= m_samplerate*wfe.nBlockAlign;

    wfe.cbSize= 0;

    debug("esrcAudioInput::StartAudioInput: nrof recording devs: %d\n", waveInGetNumDevs());

    // initialize audio input 
    MMRESULT res= waveInOpen( &m_hwi, WAVE_MAPPER, &wfe, (DWORD_PTR)waveInProc, (DWORD)this, CALLBACK_FUNCTION );
    if( res!=MMSYSERR_NOERROR )
    {
        debug("esrcAudioInput::StartAudioInput: ERROR waveInOpen: %08lx\n", res);
        m_hwi= NULL;
        return false;
    }

    res= waveInStop(m_hwi);
    if (res!=MMSYSERR_NOERROR )
    {
        debug("esrcAudioInput::StartAudioInput: ERROR waveInStop: %08lx\n", res);
        return false;
    }

    m_whlist.resize(m_nBlocks);
    m_bufferlist.resize(m_nBlocks);
    for (int i=0 ; i<m_nBlocks ; i++)
        PrepareAndSubmitBuffer(i);

    debug("esrcAudioInput::StartAudioInput: prepared and submitted buffers\n");


    res= waveInStart( m_hwi );
    if( res!=MMSYSERR_NOERROR )
    {
        debug("ERROR waveInStart: %08lx\n", res);
        return false;
    }

    debug("esrcAudioInput::StartAudioInput: waveInStart OK\n");
    return true;
}

void esrcAudioInput::StopAudioInput()
{
    if (m_hwi==NULL)
        return;

    m_bRunning= false;

    MMRESULT res= waveInStop( m_hwi );
    if( res!=MMSYSERR_NOERROR )
        debug("ERROR waveInStop: %08lx\n", res);

    res= waveInReset( m_hwi );
    if( res!=MMSYSERR_NOERROR )
        debug("ERROR waveInReset: %08lx\n", res);

    res= waveInClose( m_hwi );
    if( res!=MMSYSERR_NOERROR )
        debug("ERROR waveInClose: %08lx\n", res);
    m_hwi= NULL;

    m_queue.DumpState();

    debug("esrcAudioInput::StopAudioInput - got %d audio blocks\n", m_nAudioBlocksProcessed);
}
